﻿/**
<code>
  <author name="Zealic" email="zealic(at)gmail.com" type="Creator"/>
</code>
**/
using System;


namespace Zealib.Extensions
{
    /// <summary>
    /// Extensions for <seealso cref="System.Type"/> class.
    /// </summary>
    public static class TypeExtensions
    {
        /// <summary>
        /// Returns an indication whether the specified <paramref name="self"/> is an anonymous type.
        /// </summary>
        /// <param name="self">anonymous type.</param>
        /// <returns>true if <paramref name="self"/> is an anonymous type; otherwise, false.</returns>
        public static bool IsAnonymous(this Type self)
        {
            self.IsNotNull();
            return self.Name.StartsWith("<>f__AnonymousType");
        }

        /// <summary>
        /// Returns an indication whether the specified <paramref name="self"/> is an closure class.
        /// </summary>
        /// <param name="self">closure type.</param>
        /// <returns>true if <paramref name="self"/> is an closure type; otherwise, false.</returns>
        public static bool IsClosure(this Type self)
        {
            self.IsNotNull();
            return self.Name.StartsWith("<>c__DisplayClass");
        }

        public static bool IsImplInterface(this Type self, Type interfaceType)
        {
            self.IsNotNull();
            Type findedType = Array.Find(self.GetInterfaces(),
                       face => face == interfaceType);

            return findedType != null;
        }

        public static bool IsImplGenericInterface(this Type self, Type interfaceType)
        {
            self.IsNotNull();
            Type value;
            return IsImplGenericInterface(self, interfaceType, out value);
        }

        public static bool IsImplGenericInterface(this Type self, Type interfaceType, out Type findedInterfaceType)
        {
            self.IsNotNull();
            if (self.IsInterface) throw new ArgumentException("Can not be interface type.", "self");
            findedInterfaceType = null;

            Type findedType = Array.Find(self.GetInterfaces(),
                       face => face.IsGenericType &&
                               face.GetGenericTypeDefinition() == interfaceType);
            findedInterfaceType = findedType;
            return findedInterfaceType != null;
        }

        public static bool IsGenericSubclassOf(this Type self, Type genericTypeDefine)
        {
            self.IsNotNull();
            if (null == genericTypeDefine) throw new ArgumentNullException("genericTypeDefine");
            if (!genericTypeDefine.IsGenericTypeDefinition) throw new ArgumentException("Must be generic type define.", "genericTypeDefine");
            while (!self.IsGenericType)
            {
                self = self.BaseType;
                if (null == self) break;
            }
            if (null != self)
            {
                Type defineType = self.GetGenericTypeDefinition();
                while (defineType != null)
                {
                    if (defineType.GUID == genericTypeDefine.GUID)
                        return true;
                    defineType = defineType.BaseType;
                }
            }

            return false;
        }

        public static bool IsGenericInstanceOf(this Type self, object instance)
        {
            self.IsNotNull();
            if (null == instance) throw new ArgumentNullException("instance");
            if (!self.IsGenericTypeDefinition)
                throw new ArgumentException("Must be Generic type define.", "self");
            Type type = instance.GetType();
            return IsGenericSubclassOf(type, self);
        }

        public static bool IsStatic(this Type self)
        {
            self.IsNotNull();
            return self.IsAbstract && self.IsSealed;
        }

    }
}
